// Big integer base-10 printing library
// Copyright (c) 2008-2021 Lapo Luchini <lapo@lapo.it>

// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

var max = 10000000000000; // biggest 10^n integer that can still fit 2^53 when multiplied by 256

/**
 * Arbitrary length base-10 value.
 * @param {number} value - Optional initial value (will be 0 otherwise).
 */
class Int10 {
  buf: number[];
  constructor(value?: number) {
    this.buf = value ? [+value] : [0];
  }

  /**
   * Multiply value by m and add c.
   * @param {number} m - multiplier, must be < =256
   * @param {number} c - value to add
   */
  mulAdd(m: number, c: number) {
    // assert(m <= 256)
    var b = this.buf,
      l = b.length,
      i,
      t;
    for (i = 0; i < l; ++i) {
      t = b[i] * m + c;
      if (t < max) c = 0;
      else {
        c = 0 | (t / max);
        t -= c * max;
      }
      b[i] = t;
    }
    if (c > 0) b[i] = c;
  }

  /**
   * Subtract value.
   * @param {number} c - value to subtract
   */
  sub(c: number) {
    var b = this.buf,
      l = b.length,
      i,
      t;
    for (i = 0; i < l; ++i) {
      t = b[i] - c;
      if (t < 0) {
        t += max;
        c = 1;
      } else c = 0;
      b[i] = t;
    }
    while (b[b.length - 1] === 0) b.pop();
  }

  /**
   * Convert to decimal string representation.
   * @param {*} base - optional value, only value accepted is 10
   */
  toString(base?: any) {
    if ((base || 10) != 10) throw 'only base 10 is supported';
    var b = this.buf,
      s = b[b.length - 1].toString();
    for (var i = b.length - 2; i >= 0; --i)
      s += (max + b[i]).toString().substring(1);
    return s;
  }

  /**
   * Convert to Number value representation.
   * Will probably overflow 2^53 and thus become approximate.
   */
  valueOf() {
    var b = this.buf,
      v = 0;
    for (var i = b.length - 1; i >= 0; --i) v = v * max + b[i];
    return v;
  }

  /**
   * Return value as a simple Number (if it is <= 10000000000000), or return this.
   */
  simplify() {
    var b = this.buf;
    return b.length == 1 ? b[0] : this;
  }
}
export default Int10;
